package com.itrip.log.util;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.security.Key;
import java.security.SecureRandom;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Pattern;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

import org.apache.commons.net.telnet.TelnetClient;
import org.apache.commons.net.util.Base64;
import org.apache.log4j.FileAppender;
import org.apache.log4j.helpers.Loader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

/**
 * load config from file
 * 
 * @author @author code_czp@126.com-2015年5月12日
 */
public class Util {

	public static final String KEY_ALGORITHM = "DES";
	public static final String key = "A1B2C3D4E5F60708";
	public static final String CIPHER_ALGORITHM = "DES/ECB/PKCS5Padding";
	public static final Logger logger = LoggerFactory.getLogger(Util.class);
	public static final Map<String, JSONObject> map = new HashMap<String, JSONObject>();
	public static final Pattern P = Pattern.compile("(\\d+)");
	public static final Charset SET = Charset.forName("utf-8");

	public static JSONObject loadJSONObject(String configFile) {
		return (JSONObject) loadConfigWithCache(configFile);
	}

	public static JSONArray loadJSONArray(String configFile) {
		return (JSONArray) loadConfigWithCache(configFile);
	}

	public static JSON loadConfigWithCache(String configFile) {
		String temp = configFile.replace("classpath:", "");
		JSONObject old = map.get(configFile);
		File f = new File(temp);
		if (old == null || old.getLongValue("lastModify") < f.lastModified()) {
			JSON json = loadConfig(configFile);
			old = new JSONObject(2);
			old.put("lastModify", f.lastModified());
			old.put("json", json);
			map.put(configFile, old);
			System.out.println(configFile + " is modify");
			return json;
		}
		return (JSON) map.get(configFile).get("json");
	}

	public static JSON loadConfig(String configFile) {
		JSON res;
		String content;
		boolean isArr = false;
		try {
			content = readConfigFile(configFile);
			if (content.startsWith("[")) {
				isArr = true;
				res = JSONArray.parseArray(content);
			} else if (content.startsWith("{"))
				res = JSONObject.parseArray(content);
			else {
				JSONObject obj = new JSONObject();
				obj.put("content", content);
				res = obj;
			}
		} catch (Exception e) {
			JSONObject obj = new JSONObject();
			obj.put("content", e.toString());
			if (isArr) {
				res = new JSONArray(1);
				((JSONArray) res).add(obj);
			} else {
				res = obj;
			}
			e.printStackTrace();
		}
		return res;

	}

	private static String readConfigFile(String configFile) throws IOException {
		if (configFile.startsWith("classpath:")) {
			return readConfigFromClassPath(configFile);
		}
		String line;
		FileReader in = new FileReader(configFile);
		BufferedReader br = new BufferedReader(in);
		StringBuffer json = new StringBuffer();
		while ((line = br.readLine()) != null) {
			json.append(line);
		}
		br.close();
		return json.toString();

	}

	public static String readLastLine(String configFile) throws IOException {
		File f = new File(configFile);
		if (!f.exists())
			return null;
		String line = null;
		String lastLine = null;
		FileReader in = new FileReader(f);
		BufferedReader br = new BufferedReader(in);
		while ((line = br.readLine()) != null)
			lastLine = line;
		br.close();
		return lastLine;
	}

	public static JSONObject readLastLineToJson(String configFile) throws IOException {
		String line = readLastLine(configFile);
		if (line == null || line.trim().length() == 0)
			return new JSONObject();
		return JSONObject.parseObject(line);
	}

	private static String readConfigFromClassPath(String configFile) throws IOException {
		configFile = configFile.replace("classpath:", "");
		InputStream is = Util.class.getResourceAsStream(configFile);
		if (is == null)
			is = Util.class.getClassLoader().getResourceAsStream(configFile);
		if (is == null)
			is = ClassLoader.getSystemResourceAsStream(configFile);

		BufferedInputStream br = new BufferedInputStream(is);
		StringBuffer json = new StringBuffer();
		byte[] buf = new byte[1024];
		int size;
		while ((size = br.read(buf)) != -1) {
			json.append(new String(buf, 0, size));
		}
		br.close();

		return json.toString();
	}

	private static SecretKey keyGenerator(String keyStr) throws Exception {
		byte input[] = HexString2Bytes(keyStr);
		DESKeySpec desKey = new DESKeySpec(input);
		return SecretKeyFactory.getInstance("DES").generateSecret(desKey);
	}

	private static int parse(char c) {
		if (c >= 'a')
			return (c - 'a' + 10) & 0x0f;
		if (c >= 'A')
			return (c - 'A' + 10) & 0x0f;
		return (c - '0') & 0x0f;
	}

	// 从十六进制字符串到字节数组转换
	public static byte[] HexString2Bytes(String hexstr) {
		int j = 0;
		byte[] b = new byte[hexstr.length() / 2];
		for (int i = 0; i < b.length; i++) {
			char c0 = hexstr.charAt(j++);
			char c1 = hexstr.charAt(j++);
			b[i] = (byte) ((parse(c0) << 4) | parse(c1));
		}
		return b;
	}

	/**
	 * 加密数据
	 * 
	 * @param data
	 *            待加密数据
	 * @param key
	 *            密钥
	 * @return 加密后的数据
	 */
	public static String encrypt(String data) {
		try {
			Key deskey = keyGenerator(key);
			SecureRandom random = new SecureRandom();
			Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
			cipher.init(Cipher.ENCRYPT_MODE, deskey, random);
			byte[] results = cipher.doFinal(data.getBytes());
			return Base64.encodeBase64String(results);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static void count(Map<String, Integer> map, String key) {
		if (map.containsKey(key)) {
			map.put(key, map.get(key) + 1);
		} else {
			map.put(key, 1);
		}
	}

	/**
	 * 解密数据
	 * 
	 * @param data
	 *            待解密数据
	 * @param key
	 *            密钥
	 * @return 解密后的数据
	 */
	public static String decrypt(String data) {
		try {
			Key deskey = keyGenerator(key);
			Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
			cipher.init(Cipher.DECRYPT_MODE, deskey);
			return new String(cipher.doFinal(Base64.decodeBase64(data)));
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static List<Number> getNumber(String str) {
		List<Number> nums = new ArrayList<Number>();
		StringBuffer tmp = new StringBuffer();
		char[] arr = str.toCharArray();
		for (int i = 0; i < arr.length; i++) {
			char ch = arr[i];
			if (ch == '-' || Character.isDigit(ch)) {
				tmp.append(ch);
			} else {
				int len = tmp.length();
				if (ch == '.' && len > 0) {
					tmp.append(ch);
				} else {
					if (!(len == 0 || (len == 1 && tmp.charAt(0) == '-'))) {
						nums.add(Double.valueOf(tmp.toString()));
					}
					tmp = new StringBuffer();
				}
			}
		}
		int len = tmp.length();
		if (!(len == 0 || (len == 1 && tmp.charAt(0) == '-'))) {
			nums.add(Double.valueOf(tmp.toString()));
		}
		return nums;
	}

	public static boolean isServerAlive(String addr) {
		return isServerAlive(addr, 3000);
	}

	public static boolean isServerAlive(String addr, int timeout) {
		try {
			return InetAddress.getByName(addr).isReachable(timeout);
		} catch (Exception e) {
			logger.error("error", e);
			return false;
		}
	}

	public static boolean isPortRuning(String addr, int port) {
		try {
			TelnetClient client = new TelnetClient();
			client.connect(addr, port);
			client.disconnect();
			return true;
		} catch (Exception e) {
			logger.error("error", e);
			return false;
		}
	}

	public static boolean isUrlReachable(String url) {
		try {
			URLConnection connu = new URL(url).openConnection();
			HttpURLConnection conn = (HttpURLConnection) connu;
			conn.connect();
			conn.disconnect();
			return true;
		} catch (Exception e) {
			logger.error("error", e);
			return false;
		}
	}

	@SuppressWarnings("deprecation")
	public static long getXDayAgoTime(int days, boolean setHMS0) {
		Date now = new Date();
		if (setHMS0) {
			now.setHours(0);
			now.setMinutes(0);
			now.setSeconds(0);
		}
		now.setDate(now.getDate() - days);
		return now.getTime();
	}

	public static Number getNum(String item) {
		List<Number> num = getNumber(item);
		return num.isEmpty() ? 0 : num.get(0);
	}

	public static void close(Closeable... res) {
		for (Closeable closeable : res) {
			try {
				if (closeable != null)
					closeable.close();
			} catch (Exception e) {
				logger.error("error", e);
			}
		}
	}

	public static void close(Statement... res) {
		for (Statement closeable : res) {
			try {
				if (closeable != null)
					closeable.close();
			} catch (Exception e) {
				logger.error("error", e);
			}
		}
	}

	public static boolean isNumber(String lastStr) {
		for (char c : lastStr.toCharArray()) {
			if (!Character.isDigit(c)) {
				return false;
			}
		}
		return true;
	}

	public static int getValue(JSONObject query, String project, int defaultVal) {
		int integer = query.getIntValue(project);
		int integer2 = query.getIntValue("itrip_" + project);
		int integer3 = query.getIntValue("service_" + project);
		return integer + integer2 + integer3 + defaultVal;
	}

	public static boolean dateEqualIgnoreTime(Calendar now, Calendar date) {
		boolean yearEq = date.get(Calendar.YEAR) == now.get(Calendar.YEAR);
		boolean dateEq = date.get(Calendar.DATE) == now.get(Calendar.DATE);
		boolean monthEq = date.get(Calendar.MONTH) == now.get(Calendar.MONTH);
		return yearEq && monthEq && dateEq;
	}

	public static double timeCostSec(long startTime) {
		return timeCost(startTime) / 1000.0;
	}

	public static long timeCost(long startTime) {
		return System.currentTimeMillis() - startTime;
	}

	/**
	 * 发送消息
	 * 
	 * @param type
	 *            消息类型 短信：COMMON_SMS 邮件：COMMON_EMAIL
	 * @param receiver
	 *            接收者
	 * @param subject
	 *            主题
	 * @param content
	 *            内容
	 */
	public static void sendEmailHttp(String urlStr, String receiver, String subject, String content) {
		try {
			if (urlStr == null || urlStr.length() == 0) {
				logger.info("urlStr is empty,{}", content);
				return;
			}
			
			JSONObject context = new JSONObject();
			context.put("receiver", receiver);
			context.put("subject", subject);
			context.put("message", content);
			context.put("saveLog", "true");

			URL url = new URL(urlStr);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod("POST");
			conn.setDoOutput(true);
			conn.setDoInput(true);

			StringBuffer params = new StringBuffer();
			params.append("message=").append(context.toJSONString());
			conn.getOutputStream().write(params.toString().getBytes(SET));
			conn.getOutputStream().flush();

			String line;
			BufferedReader bos = new BufferedReader(new InputStreamReader(conn.getInputStream()));
			StringBuffer lines = new StringBuffer();
			while ((line = bos.readLine()) != null) {
				lines.append(line);
			}
			conn.disconnect();
			logger.info("send email:{} return:", receiver, lines);
		} catch (Exception e) {
			logger.error("发送消息错误！ ", e);
		}
	}

	public static org.apache.log4j.Logger getDBLogger(String name) {
		org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger("all");
		log.setAdditivity(false);
		return log;
	}

	public static String findDBPath(org.apache.log4j.Logger log) {
		String dbFilePath = null;
		Enumeration<?> appenders = log.getAllAppenders();
		while (appenders.hasMoreElements()) {
			Object next = appenders.nextElement();
			if (next instanceof FileAppender) {
				dbFilePath = ((FileAppender) next).getFile();
				break;
			}
		}
		return dbFilePath;
	}

	public static String findDBPathByConfigFile(String logName) {
		try {
			Properties props = new Properties();
			URL url = Loader.getResource("log4j.properties");
			URLConnection uConn = url.openConnection();
			InputStream istream = uConn.getInputStream();
			props.load(istream);
			istream.close();
			String property = props.getProperty("log4j.logger." + logName);
			String appenderName = property.split(",")[1];
			String file = props.getProperty("log4j.appender." + appenderName + ".File");
			return file;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
}
